package com.iflytek.demo.ability.tts;


import com.iflytek.demo.ability.AppLog;
import com.iflytek.demo.ability.Tools;
import ohos.app.Context;
import ohos.media.audio.AudioRenderer;
import ohos.media.audio.AudioRendererInfo;
import ohos.media.audio.AudioStreamInfo;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.LinkedList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * pcm 播放器 用于动态添加音频数据，实时播放
 *
 * @author zhhuang10
 * @version 1.0
 * @date 2020/10/30
 */
public class TtsPcmPlayer {
    /**
     * TAG
     */
    private final String TAG = "TtsPcmPlayer";
    /**
     * AudioRenderer
     */
    private AudioRenderer audioRenderer;
    /**
     * 缓存大小
     */
    private int bufferSize;

    /**
     * 内存缓存大小 ：缓存 0.5 s数据
     */
    private int MAX_UNCONSUMED_BYTE;
    /**
     * mAudioTrackLock
     */
    private final Object mAudioTrackLock = new Object();
    // Lock
    private final Lock mListLock = new ReentrantLock();
    // AudioRenderer 获取数据 的 Condition
    private final Condition mTakeData = mListLock.newCondition();
    // 接收的数据缓存 LinkedList
    private final LinkedList<ListEntry> mDataBufferList = new LinkedList<>();
    // 未播放的数据数量
    private int mUnconsumedBytes;
    // 添加数据完毕标识
    private volatile boolean mFinishPut;
    // 缓存文件
    private final File cacheFile;
    // 缓存文件的游标
    private volatile int cacheFileCursor;
    /**
     * 播放完毕回调
     */
    private PlayAudioListener mListener;
    private int sampleRateInHz;
    private int channels;
    private int pcmBit;

    /**
     * 构造函数
     *
     * @param context Context
     */
    public TtsPcmPlayer(Context context) {
        this(context, 16000);
    }

    /**
     * 播放pcm
     *
     * @param file pcm文件
     */
    private void playPcm(File file) {
        if (file == null || !file.exists()) {
            return;
        }
        AudioStreamInfo streamInfo = new AudioStreamInfo.Builder()
                // 16kHz
                .sampleRate(16000)
                // 混音
                .audioStreamFlag(AudioStreamInfo.AudioStreamFlag.AUDIO_STREAM_FLAG_MAY_DUCK)
                // 16-bit PCM
                .encodingFormat(AudioStreamInfo.EncodingFormat.ENCODING_PCM_16BIT)
                // 单声道输出
                .channelMask(AudioStreamInfo.ChannelMask.CHANNEL_OUT_MONO)
                // 媒体类音频
                .streamUsage(AudioStreamInfo.StreamUsage.STREAM_USAGE_MEDIA)
                .build();

        AudioRendererInfo audioRendererInfo = new AudioRendererInfo.Builder().audioStreamInfo(streamInfo)
                // pcm格式的输出流
                .audioStreamOutputFlag(AudioRendererInfo.AudioStreamOutputFlag.AUDIO_STREAM_OUTPUT_FLAG_DIRECT_PCM)
                .bufferSizeInBytes(1280)
                // false表示分段传输buffer并播放，true表示整个音频流一次性传输到HAL层播放
                .isOffload(false)
                .build();

        AudioRenderer renderer = new AudioRenderer(audioRendererInfo, AudioRenderer.PlayMode.MODE_STREAM);
        renderer.start();
        try {
            FileInputStream inputStream = new FileInputStream(file);
            byte[] temp = new byte[1280];
            while (inputStream.available() > temp.length) {
                int read = inputStream.read(temp);
                renderer.write(temp, 0, read);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 构造函数
     *
     * @param context        Context
     * @param sampleRateInHz 采样率
     */
    public TtsPcmPlayer(Context context, int sampleRateInHz) {
        this.sampleRateInHz = sampleRateInHz;
        this.channels = 1;
        this.pcmBit = 16;
        init();
        bufferSize = AudioRenderer.getMinBufferSize(
                sampleRateInHz,
                AudioStreamInfo.EncodingFormat.ENCODING_PCM_16BIT,
                AudioStreamInfo.ChannelMask.CHANNEL_OUT_MONO);
        cacheFile = new File(context.getExternalCacheDir(), System.currentTimeMillis() + ".pcm");
        if (cacheFile.exists()) {
            cacheFile.delete();
        }
        try {
            cacheFile.createNewFile();
        } catch (IOException e) {
            AppLog.e(TAG, "create tts cacheFile failed");
            e.printStackTrace();
        }
    }

    private void init() {
        // 内存缓存大小 ：缓存 0.5 s数据
        MAX_UNCONSUMED_BYTE = (int) (0.5 * sampleRateInHz * channels * pcmBit / 8);
    }


    /**
     * 开始播放
     */
    public void startPlay() {
        startPlay(AudioStreamInfo.StreamUsage.STREAM_USAGE_MEDIA);
    }

    /**
     * 开始播放
     *
     * @param inputStreamUsage 音频源类型
     */
    public void startPlay(AudioStreamInfo.StreamUsage inputStreamUsage) {
        mFinishPut = false;
        cacheFileCursor = 0;
        AudioStreamInfo streamInfo = new AudioStreamInfo.Builder()
                // 16kHz
                .sampleRate(sampleRateInHz)
                // 混音
                .audioStreamFlag(AudioStreamInfo.AudioStreamFlag.AUDIO_STREAM_FLAG_MAY_DUCK)
                // 16-bit PCM
                .encodingFormat(AudioStreamInfo.EncodingFormat.ENCODING_PCM_16BIT)
                // 单声道输出
                .channelMask(AudioStreamInfo.ChannelMask.CHANNEL_OUT_MONO)
                // 媒体类音频
                .streamUsage(inputStreamUsage)
                .build();

        AudioRendererInfo audioRendererInfo = new AudioRendererInfo.Builder().audioStreamInfo(streamInfo)
                // pcm格式的输出流
                .audioStreamOutputFlag(AudioRendererInfo.AudioStreamOutputFlag.AUDIO_STREAM_OUTPUT_FLAG_DIRECT_PCM)
                .bufferSizeInBytes(1280)
                // false表示分段传输buffer并播放，true表示整个音频流一次性传输到HAL层播放
                .isOffload(false)
                .build();

        audioRenderer = new AudioRenderer(audioRendererInfo, AudioRenderer.PlayMode.MODE_STREAM);
        play();
        if (mListener != null) {
            mListener.onPlayStart();
        }
    }


    /**
     * 添加数据
     */
    public void put(byte[] buffer) {
        try {
            mListLock.lock();
            if (mFinishPut) {
                return;
            }
            if (cacheFileCursor == cacheFile.length() && mUnconsumedBytes < MAX_UNCONSUMED_BYTE) {
                mDataBufferList.add(new ListEntry(buffer));
                mUnconsumedBytes += buffer.length;
                cacheFileCursor += buffer.length;
            }
            // 缓存到文件中
            Tools.append(cacheFile, buffer);
            mTakeData.signal();
        } finally {
            mListLock.unlock();
        }
    }


    /**
     * 获取数据给播放器
     */
    private byte[] take() {
        try {
            // 上锁
            mListLock.lock();
            // 从缓存文件拿数据到mDataBufferList
            if (mDataBufferList.isEmpty() && cacheFileCursor < cacheFile.length()) {
                int size = (int) Math.min(cacheFile.length() - cacheFileCursor, MAX_UNCONSUMED_BYTE);
                byte[] bytes = Tools.readData(cacheFile, cacheFileCursor, size);
                mDataBufferList.add(new ListEntry(bytes));
                cacheFileCursor = cacheFileCursor + size;
            }
            if (mDataBufferList.isEmpty() && mFinishPut) {
                return null;
            }
            while (mDataBufferList.isEmpty()) {
                // 合成数据已经播放完，等待引擎返回数据时 线程等待
                try {
                    mTakeData.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            ListEntry entry = mDataBufferList.poll();
            if (entry == null) {
                return null;
            }
            mUnconsumedBytes -= entry.mBytes.length;
            return entry.mBytes;
        } finally {
            mListLock.unlock();
        }
    }

    /**
     * AudioRenderer 的 bufferSize
     */
    public int getBufferSize() {
        return bufferSize;
    }

    /**
     * 播放音频
     */
    private void play() {
        Runnable runnable = () -> {
            try {
                if (!isPlaying()) {
                    audioRenderer.start();
                }
                resetCursor();
                byte[] buffer;
                while ((buffer = take()) != null && isPlaying()) {
                    writeToAudioTrack(audioRenderer, buffer);
                }
                // 播放结束回调
                if (mFinishPut && cacheFileCursor == cacheFile.length()) {
                    audioRenderer.write(new byte[getBufferSize()], 0, getBufferSize());
                    if (cacheFile != null && cacheFile.exists()) {
                        cacheFile.delete();
                    }
                    if (mListener != null) {
                        mListener.onPlayCompletion();
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        };
        new Thread(runnable).start();
    }

    /**
     * 播放器前重置播放位置
     */
    private void resetCursor() {
        try {
            mListLock.lock();
            mDataBufferList.clear();
            cacheFileCursor = audioRenderer.getPosition() * channels * pcmBit / 8;
//            cacheFileCursor = audioRenderer.getPlaybackHeadPosition() * channels * pcmBit / 8;
        } finally {
            mListLock.unlock();
        }
    }

    /**
     * 写入数据到播放器
     */
    private void writeToAudioTrack(AudioRenderer audioTrack, byte[] bytes) {
        int count = 0;
        while (count < bytes.length) {
            boolean written = audioTrack.write(bytes, count, bytes.length);
            if (!written) {
                return;
            }
            count += bytes.length;
        }
    }


    /**
     * 播放器是否在播放
     */
    public boolean isPlaying() {
        synchronized (mAudioTrackLock) {
            if (audioRenderer == null) {
                return false;
            }
            return audioRenderer.getState() == AudioRenderer.State.STATE_PLAYING;
        }
    }

    /**
     * 停止播放
     */
    public void stop() {
        mFinishPut = true;
        synchronized (mAudioTrackLock) {
            if (audioRenderer != null) {
                audioRenderer.stop();
            }
            if (cacheFile != null && cacheFile.exists()) {
                cacheFile.delete();
            }
        }
    }

    /**
     * 暂停
     */
    public void pause() {
        synchronized (mAudioTrackLock) {
            if (audioRenderer != null && isPlaying()) {
                audioRenderer.pause();
            }
        }
    }

    /**
     * 播放器是否暂停
     */
    public boolean isPause() {
        synchronized (mAudioTrackLock) {
            if (audioRenderer == null) {
                return false;
            }
            return audioRenderer.getState() == AudioRenderer.State.STATE_PAUSED;
        }
    }

    /**
     * 播放器继续
     */
    public void resume() {
        synchronized (mAudioTrackLock) {
            if (!isPause()) {
                return;
            }
            play();
        }
    }


    /**
     * 播放器 release
     */
    public void release() {
        synchronized (mAudioTrackLock) {
            if (audioRenderer != null) {
                audioRenderer.release();
                audioRenderer = null;
            }
            if (cacheFile != null && cacheFile.exists()) {
                cacheFile.delete();
            }
        }
    }

    /**
     * 设置播放器音量
     */
    public void setVolume(float volume) {
        if (audioRenderer != null) {
            audioRenderer.setVolume(volume);
        }
    }

    /**
     * 给播放器添加数据完毕
     */
    public void finishPut() {
        mFinishPut = true;
    }

    /**
     * 设置播放回调
     */
    public void setPlayListener(PlayAudioListener listener) {
        mListener = listener;
    }

    /**
     * 每一包数据的包装
     */
    private static final class ListEntry {
        final byte[] mBytes;

        ListEntry(byte[] bytes) {
            mBytes = bytes;
        }
    }

}
